import cv2
import mediapipe as mp
import numpy as np
import pygame
import os
import random
from orange import Orange
import time
import paho.mqtt.client as mqtt
import json
import threading

# 定义全局变量
idleflag = True #当此标志变量为False时，播放空闲照片和操作示例说明，单手直举起时表示开始
startnum = 2 #开始则设置为10秒倒计时
endnum = 2 #结束则设置为10秒倒计时
checknum = 600 #播放摄像头，没有检查到有人举手，减为0以后，再提示
gamenum = 2000 #游戏水果
gamespeed = 2 #速度倍数
flashnum = 0.5 #碰到水果时延迟的时间再确认为碰到
is_button_click = False
inkIf = False

# 确定PNG图片在视频帧上的位置
sleepy = 0
x = 10  # X坐标
y = 10  # Y坐标
# 生成一个范围在1到10（包括1和10）的随机整数
x = random.randint(1, 600)
myscore = 0
imagesize = 80

# 初始化图片、声音 开始
# 获取当前Python文件的绝对路径
current_script_path = os.path.abspath(__file__)
# 获取当前Python文件所在的目录
current_directory = os.path.dirname(current_script_path)
print("文件:",current_directory+"\\orange_transparent.png")

# 显示游戏示范图片
image_path = 'background.jpg'  # 替换为你的图片路径
background_image = cv2.imread(current_directory+"\\footage\\image\\"+image_path, cv2.IMREAD_UNCHANGED)
girlok01 = cv2.imread(current_directory+"\\footage\\image\\"+image_path , cv2.IMREAD_UNCHANGED)
girlfail01 = cv2.imread(current_directory+"\\footage\\image\\"+image_path , cv2.IMREAD_UNCHANGED)



# 加载音效文件
# 初始化pygame mixer
pygame.mixer.init()
sound_fruit = pygame.mixer.Sound(current_directory+"\\footage\\sound\\splatter.mp3")
sound_iceBanana = pygame.mixer.Sound(current_directory+"\\footage\\sound\\iceBanana.mp3")
sound_potion = pygame.mixer.Sound(current_directory+"\\footage\\sound\\potion.ogg")
sound_boom = pygame.mixer.Sound(current_directory+"\\footage\\sound\\boom.mp3")
sound_durian = pygame.mixer.Sound(current_directory+"\\footage\\sound\\durian.wav")
# 加载图像并调整大小。
orange_img0 = cv2.imread(current_directory+"\\footage\\image\\fruit\\orange.png", cv2.IMREAD_UNCHANGED)
orange_explosion = cv2.imread(current_directory+"\\explosion_2.png", cv2.IMREAD_UNCHANGED)
watermelon_img0 = cv2.imread(current_directory+"\\footage\\image\\fruit\\watermelon.png", cv2.IMREAD_UNCHANGED)
watermelon_explosion1 = cv2.imread(current_directory+"\\footage\\image\\fruit\\watermelon-1.png", cv2.IMREAD_UNCHANGED)
watermelon_explosion2= cv2.imread(current_directory+"\\footage\\image\\fruit\\watermelon-2.png", cv2.IMREAD_UNCHANGED)
peach_img0 = cv2.imread(current_directory+"\\footage\\image\\fruit\\peach.png", cv2.IMREAD_UNCHANGED)
peach_explosion = cv2.imread(current_directory+"\\footage\\image\\fruit\\peach-1.png", cv2.IMREAD_UNCHANGED)
iceBanana_img0 = cv2.imread(current_directory+"\\footage\\image\\item\\iceBanana.png", cv2.IMREAD_UNCHANGED)
potion_img0 = cv2.imread(current_directory+"\\footage\\image\\item\\potion.png", cv2.IMREAD_UNCHANGED)
durian_img0 = cv2.imread(current_directory+"\\footage\\image\\fruit\\durian.png", cv2.IMREAD_UNCHANGED)
ink_img0 = cv2.imread(current_directory+"\\footage\\image\\item\\ink.png", cv2.IMREAD_UNCHANGED)

boom1 = cv2.imread(current_directory+"\\footage\\image\\fruit\\boom.png", cv2.IMREAD_UNCHANGED)
wuya2 = cv2.imread(current_directory+"\\footage\\image\\fruit\\boom.png", cv2.IMREAD_UNCHANGED)

orange_img = orange_img0  #表示当前显示的水果图像

# 调整橘子图像大小
orange_img0 = cv2.resize(orange_img0, (imagesize, imagesize))  # 将橘子图像调整为imagesizeximagesize像素    
orange_explosion = cv2.resize(orange_explosion, (imagesize, imagesize))  # 将橘子图像调整为imagesizeximagesize像素
watermelon_img0 = cv2.resize(watermelon_img0 , (imagesize, imagesize))
watermelon_explosion1 = cv2.resize(watermelon_explosion1 , (imagesize, imagesize))
watermelon_explosion2 = cv2.resize(watermelon_explosion2 , (imagesize, imagesize))
peach_img0 = cv2.resize(peach_img0, (imagesize, imagesize))
peach_explosion = cv2.resize(peach_explosion, (imagesize, imagesize))
iceBanana_img0 = cv2.resize(iceBanana_img0,(imagesize,imagesize))
potion_img0 = cv2.resize(potion_img0,(imagesize,imagesize))
durian_img0 = cv2.resize(durian_img0,(imagesize,imagesize))

boom1 = cv2.resize(boom1, (imagesize, imagesize))
wuya2 = cv2.resize(wuya2, (imagesize, imagesize))
# 获取叠加图像的尺寸
overlay_height, overlay_width = orange_img0.shape[:2]



# 初始化摄像头
cap = cv2.VideoCapture(0)
# 确保摄像头被成功打开
if not cap.isOpened():
    print("无法打开摄像头")
    exit()

# 你需要根据你的摄像头支持的分辨率列表来设置
desired_width = 1024
desired_height = 1024
# 设置宽度
cap.set(cv2.CAP_PROP_FRAME_WIDTH, desired_width)
# 设置高度
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, desired_height)
# 获取实际设置的分辨率
actual_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
actual_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# print(actual_width,actual_height)
# background_image = cv2.resize(background_image, (actual_width,actual_height ))  # 将橘子图像调整为imagesizeximagesize像素   

ink_img0 = cv2.resize(ink_img0,(actual_width,actual_height))

#使用算法包进行姿态估计时设置的参数
mp_pose = mp.solutions.pose
# 创建计算图
mp_drawing = mp.solutions.drawing_utils


# 定义姿态对象
pose = mp_pose.Pose(min_detection_confidence=0.5,min_tracking_confidence=0.8)

# 随机数，以权重的方式决定生成的下一个实体是什么
fruit_kind = 0
# 初始化实体列表
entities = []

#新增一个掉落物
def createfruit():
    #初始化一个实体

    # 随机生成一个数，根据权重决定实体类型
    fruit_kind = random.randint(0, 100)
    if fruit_kind < 30:
        newobj_img = orange_img0
        image = "orange"
    elif fruit_kind < 45:
        newobj_img = watermelon_img0
        image = "watermelon"
    elif fruit_kind < 60:
        newobj_img = peach_img0
        image = "peach"
    elif fruit_kind < 70:
        newobj_img = iceBanana_img0
        image = "iceBanana"
    # 原：70  测试中
    elif fruit_kind < 80:
        newobj_img = potion_img0
        image = "potion"
    elif fruit_kind < 90:
        newobj_img = durian_img0
        image = "durian"
    else:
        newobj_img = boom1
        image = "boom"

    #  上述代码在掉落生成时被复用

    newobj = Orange(newobj_img, orange_explosion, imagesize, random.randint(1, actual_width - imagesize), 0, actual_width, actual_height, image)
    # 将这个实体添加到列表里
    entities.append(newobj)

def update_game_logic():
    global entities, gamenum, myscore, gamespeed,inkIf

    ret, frame = cap.read()
    if not ret:
        return  # 如果没有成功读取帧，直接返回

    frame = cv2.flip(frame, 1)  # 翻转图像
    results = pose.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))  # 姿态估计

    for entity in entities:
        entity.addyy(gamespeed)  # 更新实体的垂直位置
        entity.checkoranger(frame, results)  # 检查是否碰到水果
        if entity.explosionflag:
            explose_floatimae(entity)  # 如果实体被击中，处理爆炸逻辑
        addoranger(frame, entity)
    addscore(frame)  # 将积分显示在帧中
    # 其他游戏逻辑...

def show_ink_effect(frame, duration=3000):
    # 确保墨水效果可以独立于游戏主循环运行
    global actual_width, actual_height
    ink_img0 = cv2.imread(current_directory + "\\footage\\image\\item\\ink.png", cv2.IMREAD_UNCHANGED)
    ink_img_resized = cv2.resize(ink_img0, (actual_width, actual_height))

    start_time = time.time()
    while time.time() - start_time < duration / 1000:
        # 更新游戏逻辑
        update_game_logic()

        if ink_img_resized.shape[2] == 4:
            ink_img_resized = cv2.cvtColor(ink_img_resized, cv2.COLOR_BGRA2BGR)
        # 显示墨水效果
        for y in range(actual_height):
            for x in range(actual_width):
                if ink_img_resized[y, x, :].tolist() != [0, 0, 0]:  # 假设背景色为黑色
                    frame[y, x] = ink_img_resized[y, x]
        cv2.imshow('frame', frame)
        cv2.waitKey(1)
    inkIf = False

def addoranger(frame,myobj):
    global myscore
    global gamenum
    global fruit_kind

    x = myobj.xx
    y = myobj.yy

    orange_img = myobj.noworange


    # 调整叠加图像的大小以匹配视频帧的大小
    overlay_resized = cv2.resize(orange_img, (overlay_width, overlay_height))

    if x + overlay_width > actual_width:
        x = 0
    if y + overlay_height > actual_height :
        y = 0
        # 随机生成掉落的位置
        x = random.randint(1, actual_width-myobj.imagesize)

        gamenum = gamenum - 1
        # myscore = myscore - 1

        # 随机生成一个数，根据权重决定实体类型
        fruit_kind = random.randint(0, 100)
        if fruit_kind < 30:
            newobj_img = orange_img0
            image = "orange"
        elif fruit_kind < 45:
            newobj_img = watermelon_img0
            image = "watermelon"
        elif fruit_kind < 60:
            newobj_img = peach_img0
            image = "peach"
        elif fruit_kind < 70:
            newobj_img = iceBanana_img0
            image = "iceBanana"
        # 原：70  测试中
        elif fruit_kind < 80:
            newobj_img = potion_img0
            image = "potion"
        elif fruit_kind < 90:
            newobj_img = durian_img0
            image = "durian"
        else:
            newobj_img = boom1
            image = "boom"

        myobj.image = image
        myobj.orange_img = newobj_img
        myobj.xx = x

    if overlay_resized.shape[2] == 3:  # 检查是否只有RGB三个通道
        # 创建一个全1的透明度通道
        alpha_channel = np.ones((overlay_resized.shape[0], overlay_resized.shape[1]), dtype=overlay_resized.dtype) * 255
        # 将透明度通道添加到图像中
        overlay_resized = np.concatenate((overlay_resized, alpha_channel[:, :, np.newaxis]), axis=2)

    # 创建一个透明度掩码
    alpha_mask = overlay_resized[:, :, 3] / 255.0
    alpha_mask = cv2.resize(alpha_mask, (overlay_width, overlay_height), interpolation=cv2.INTER_AREA)
    alpha_mask = np.stack([alpha_mask] * 3, axis=-1)

    # 在视频帧上叠加图像
    roi = frame[y:y + overlay_height, x:x + overlay_width]
    roi = cv2.add(roi * (1 - alpha_mask), overlay_resized[:, :, :3] * alpha_mask)


    # 将叠加后的区域写回视频帧
    frame[y:y + overlay_height, x:x + overlay_width] = roi
    myobj.yy = y
    myobj.xx = x

#爆炸判断
def explose_floatimae(myobj):
    global sleepy
    global myscore
    global gamenum
    global flashnum
    global fruit_kind
    global inkIf

    sleepy = sleepy + 1
    if sleepy>flashnum:
        sleepy = 0
        myobj.xx = random.randint(1, actual_width-myobj.imagesize)
        myobj.yy = 0
        gamenum = gamenum - 1
        if myobj.image == "boom":
            myscore = myscore - 1
        elif myobj.image == "watermelon":
            # 西瓜拥有少量额外加分并且增加掉落物数量的能力
            myscore = myscore + 2
            # 测试分裂动画，不生效能力
            # ret, frame = cap.read()
            # frame = cv2.flip(frame, 1)
            createfruit()
            # myobj.noworange = cv2.imread(current_directory + "\\.png", cv2.IMREAD_UNCHANGED)
        elif myobj.image == "peach":
            # 桃子拥有较多额外加分并且减少掉落物数量的能力,但不能减少至0
            myscore = myscore + 5
            if len(entities) > 1:  # 确保列表中不止一个实体
                # 随机选择另一个实体进行移除
                index_to_remove = random.choice([i for i in range(len(entities)) if entities[i] is not myobj])
                entities.pop(index_to_remove)
        elif myobj.image == "iceBanana":
            # 冰香蕉会让地鼠停留3s
            on_button_click("冰冻陷阱")
        elif myobj.image == "potion":
            # 药水会让无害地鼠的生命增加，地鼠玩家需要敲打两次才算敲中
            # ret, frame = cap.read()
            # frame = cv2.flip(frame, 1)
            # show_ink_effect(frame, duration=3000)
            # inkIf = True
            on_button_click("药水")
        elif myobj.image == "durian":
            # 榴莲会让地鼠变为有害地鼠
            on_button_click("榴莲")
        else:
            myscore = myscore + 1
    entities.remove(myobj)
    createfruit()
    # 检查是否有音乐正在播放
    if not pygame.mixer.music.get_busy():
        # 如果没有音乐正在播放，播放a.mp3
        if myobj.image == "boom":
            sound_boom.play()
        elif myobj.image == "iceBanana":
            # 播放第一段音效
            sound_iceBanana.play()
        elif myobj.image == "potion":
            sound_potion.play()
        elif myobj.image == "durian":
            sound_durian.play()
        else:
            sound_fruit.play()

def addscore(frame):
    # 在帧上添加文字
    global myscore
    global gamenum

    text = 'EndOfOrange:'+str(gamenum) + '    Myscore:' + str(myscore)
    font = cv2.FONT_HERSHEY_SIMPLEX
    position = (40, 50)  # 文字在帧上的位置（左下角的点）
    font_scale = 1
    font_color = (255, 255, 255)  # 白色
    line_type = 2

    cv2.putText(frame, text, position, font, font_scale, font_color, line_type)

def showTeach_idle(frame):
    global idleflag
    global startnum
    global checknum
    global background_image

    if idleflag == False:
        height, width = frame.shape[:2]
        png_image_resized = cv2.resize(background_image, (width, height))
        overlay = png_image_resized[:, :, :3]
        # 直接将PNG图片覆盖到视频帧上
        frame[:] = overlay

        text = 'Begin:' + str(startnum)
        font = cv2.FONT_HERSHEY_SIMPLEX
        position = (200, 200)  # 文字在帧上的位置（左下角的点）
        font_scale = 3
        font_color = (0,  0,255)  # 白色
        line_type = 2

        cv2.putText(frame, text, position, font, font_scale, font_color, line_type)

        # 显示结果帧
        cv2.imshow('frame', frame)

        startnum = startnum - 1
        cv2.waitKey(1) #不能少了这一行，否则图显示不出来
        # 等待一秒钟
        time.sleep(1)

        # time.sleep(10)
    if startnum <= 0:
        # idleflag = False
        checknum = 600


# 下一关
def show_end(frame):
    global idleflag
    global endnum
    global checknum
    global background_image
    global myscore
    global gamenum
    global gamespeed
    global flashnum

    if gamenum <= 0 or myscore >1000:
        height, width = frame.shape[:2]

        png_image_resized = cv2.resize(girlfail01, (width, height))
        text = 'Game over: You are Fail'
        if myscore > 10:
            text = 'Next Level: '+str(gamespeed+1)
            png_image_resized = cv2.resize(girlok01, (width, height))
            idleflag = False
            # print(idleflag)

        overlay = png_image_resized[:, :, :3]
        # 直接将PNG图片覆盖到视频帧上
        frame[:] = overlay

        font = cv2.FONT_HERSHEY_SIMPLEX
        position = (100, 200)  # 文字在帧上的位置（左下角的点）
        font_scale = 2
        font_color = (0,  0,255)  # 白色
        line_type = 2

        cv2.putText(frame, text, position, font, font_scale, font_color, line_type)

        # 显示结果帧
        cv2.imshow('frame', frame)

        endnum = endnum - 1
        cv2.waitKey(1) #不能少了这一行，否则图显示不出来
        # 等待一秒钟
        time.sleep(1)

        # time.sleep(10)

    if endnum <= 0:
        if myscore > 10:
            idleflag = False
            gamespeed = gamespeed + 1
        else:
            idleflag = True
        endnum = 7
        myscore = 0
        checknum = 600
        gamenum = 60
        if gamespeed > 4:
            flashnum = -1

        # print(idleflag)


# 识别举右手以开始游戏
def checkStart(results):
    global idleflag
    global startnum
    global gamenum
    global endnum

    if idleflag == False:
        return

    if results.pose_landmarks:
        # 从results中获取右手坐标，20，16，14，12，左手：11，13，15，17，从上到下
        # nose_landmark = results.pose_landmarks.landmark[5]
        # print(results.pose_landmarks.landmark[17].visibility,results.pose_landmarks.landmark[17].y,results.pose_landmarks.landmark[11].y)
        if results.pose_landmarks.landmark[17].visibility>0.8 and  results.pose_landmarks.landmark[17].y < results.pose_landmarks.landmark[15].y and results.pose_landmarks.landmark[15].y < results.pose_landmarks.landmark[13].y and results.pose_landmarks.landmark[13].y < results.pose_landmarks.landmark[11].y   :
            idleflag = False
            startnum = 2
            gamenum = 100
            endnum = 5
            # print("OK")

#测试区-----
# 定义按钮的属性
button_color = (208, 224, 231)  #
button_height = 50
button_width = 200
button_y = actual_height // 2  # 垂直居中
button_x = (actual_width - button_width) // 2  # 水平居中

def draw_button(frame, text='开始游戏'):
    # 绘制按钮的矩形
    cv2.rectangle(frame, (button_x, button_y), (button_x + button_width, button_y + button_height), button_color, cv2.FILLED)
    # 在按钮上添加文字
    cv2.putText(frame, 'Press s to start the game', (button_x + button_width // 2 - 20, button_y + button_height // 2 + 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 0), 2)

def checkStart(frame):
    global idleflag
    global startnum

    draw_button(frame)  # 绘制按钮
    cv2.imshow('frame', frame)
    k = cv2.waitKey(1) & 0xFF

    # 检测鼠标点击事件
    if k == 27:  # 按ESC退出
        exit()
    elif k == ord('s'):  # 按's'键模拟点击
        idleflag = False
        startnum = 2
    elif k == ord('q'):  # 按'q'键模拟点击
        exit()
    elif cv2.getWindowProperty('frame', cv2.WND_PROP_VISIBLE) >= 1:  # 检查窗口是否可见
        mouse_x, mouse_y = cv2.getWindowImageRect('frame')[:2]  # 获取鼠标坐标
        if button_x <= mouse_x <= button_x + button_width and button_y <= mouse_y <= button_y + button_height:
            if k == 1:  # 左键点击
                idleflag = False
                startnum = 2

def update_frame():
    global x, y, orange_img, checknum, gamenum, endnum, myscore,inkIf

    if len(entities) == 0:
        createfruit()
        createfruit()
        createfruit()
        createfruit()

    while(True):
        # 从摄像头读取一帧
        ret, frame = cap.read()
        # 180度翻转图像
        frame = cv2.flip(frame, 1)

        # 姿态估计
        results = pose.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))

        if idleflag:
            checkStart(frame)  # 调用新的checkStart函数
        else:
            if startnum > 0:
                showTeach_idle(frame)
                continue

            if (gamenum <= 0 and endnum > 0) or myscore > 1000:
                show_end(frame)
                continue

            # 判断每一个实体是否碰到水果
            for entity in entities:
                entity.addyy(gamespeed)
                entity.checkoranger(frame, results)
                if entity.explosionflag == True:
                    explose_floatimae(entity)
                addoranger(frame, entity)

            addscore(frame)  # 将积分显示在帧中

            # 检查是否需要显示墨水效果
            if inkIf:  # 替换为实际触发墨水效果的条件
                show_ink_effect(frame)
                inkIf = False

        cv2.imshow('frame', frame)

        if cv2.waitKey(10) & 0xFF == ord('q'):  # 按q退出
            break
    cap.release()

# 连接成功回调
def on_connect(client, userdata, flags, reason_code, properties):
    print('Connected with result code ' + str(reason_code))
    client.subscribe('playserver/+')

# 消息接收回调
def on_message(client, userdata, msg):
    global gamespeed
    global inkIf

    # 将字节类型的payload转换为字符串
    newstr = str(msg.payload.decode())
    print(msg.topic + " " + newstr)

    # 解析JSON数据
    try:
        data = json.loads(newstr)
        # 打印解析后的数据
        print("Room Name:", data.get("roomname"))
        print("Play Type:", data.get("playtype"))
        print("User ID:", data.get("userid"))
        print("Action Type:", data.get("actiontype"))
        print("Command:", data.get("command"))
        print("Value:", data.get("value"))
        print("Daoju Label:", data.get("daojuLabel"))

        # 调用mqtt_publish函数，将解析后的数据转发
        mqtt_publish(data)
        if data.get("daojuLabel") == "秒表":
            # 增加物体掉落速度
            print("秒表")
            gamespeed = gamespeed + 1
        elif data.get("daojuLabel") == "墨水":
            # 墨水遮挡屏幕，游戏停滞三秒
            ret, frame = cap.read()
            frame = cv2.flip(frame, 1)
            # show_ink_effect(frame, duration=3000)
            inkIf = True
            print("墨水")

    except json.JSONDecodeError:
        print("Error decoding JSON")

def mqtt_publish(data):
    global is_button_click
    if is_button_click:
        mqttstr = '{"roomname":"boby","playtype":"dishu","userid":"' + username + '",' \
                                    '"actiontype":"hit","command":"hithole","daojuLabel":"' + data['daojuLabel'] + '"}'
    else:
        message = {
            "message": "您的好友" + username + "已接收到您触发的道具" + data['daojuLabel'] + "的效果"
        }
        # 将字典转换为JSON字符串
        mqttstr = json.dumps(message, ensure_ascii=False)  # 正确使用json.dumps

    # 发布到响应主题
    client.publish('playserverresponse/boby', payload=mqttstr, qos=0)

    # 重置全局变量
    is_button_click = False

# 生成一个随机的用户ID
username = 'play'+str(int(round(time.time() * 1000)))

# 创建MQTT客户端
client = mqtt.Client(client_id='serverplay', callback_api_version=mqtt.CallbackAPIVersion.VERSION2)
# 指定回调函数
client.on_connect = on_connect
client.on_message = on_message

# 设置用户名和密码
client.username_pw_set('play', 'aaaa1234')

# 建立连接
client.connect('120.55.114.101', 1883, 600)

# 按钮点击事件处理函数
def on_button_click(daojuLabel):
    global is_button_click
    is_button_click = True  # 标记为按钮点击事件
    mqtt_publish({"daojuLabel": daojuLabel})  # 发送信息
# 启动MQTT客户端循环
client.loop_start()

if __name__ == '__main__':
    update_frame()